package com.spring.netty;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.spring.http.HttpContext;
import com.spring.http.HttpContextFactory;
import com.spring.http.HttpUtils;
import com.spring.http.ReturnData;
import com.spring.task.TaskDispatcher;
import com.spring.task.TaskEvent;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.CharsetUtil;

/**
 * 
 * @author Administrator
 *
 */
public class HttpServerHandler extends SimpleChannelInboundHandler<Object> {

	private final static Logger logger = LogManager.getLogger(HttpServerHandler.class.getName());
	private WebSocketServerHandshaker handshaker;

	@Override
	public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {

		// 传统的HTTP接入
		if (msg instanceof FullHttpRequest) {
			handleHttpRequest(ctx, (FullHttpRequest) msg);
		}

		// WebSocket接入
		else if (msg instanceof WebSocketFrame) {
			handleWebSocketFrame(ctx, (WebSocketFrame) msg);
		}

	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush();
	}

	private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
		// 如果HTTP解码失败，返回HHTP异常
		if (!req.getDecoderResult().isSuccess()) {
			// 抛出异常
			sendHttpResponse(ctx, req, HttpUtils.setRes(ReturnData.getData(500, "服务出错", null,"1")));
			return;
		}

		// 非WebSocket握手，处理HTTP请求
		if ((!"websocket".equals(req.headers().get("Upgrade")))) {
			HttpContext context = HttpContextFactory.newInstance();// 初始化httpContext
			// 将req中的参数放入httpContext
			context.setMethod(HttpContext.HTTP_GET);
			if (req.getMethod() == HttpMethod.POST)
				context.setMethod(HttpContext.HTTP_POST);
			// 判断数据CONTENT_TYPE
			if ("application/json".equals(req.headers().get(HttpHeaders.Names.CONTENT_TYPE))) {
				context.setDataType(HttpContext.JSON_DATA);
			} else {
				context.setDataType(HttpContext.FORM_DATA);
			}
			context.setCtx(ctx);
			context.setReq(req);
			context.setUri(req.getUri());
			
			TaskEvent event = new TaskEvent();
			event.setContext(context);
			TaskDispatcher.submitRequest(event);

		} else {
			// 构造握手响应返回，本机测试
			WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
					"ws://localhost:8080/websocket", null, false);
			handshaker = wsFactory.newHandshaker(req);
			if (handshaker == null) {
				WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
			} else {
				handshaker.handshake(ctx.channel(), req);
			}

		}
	}

	private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {

		// 判断是否是关闭链路的指令
		if (frame instanceof CloseWebSocketFrame) {
			handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
			return;
		}

		// 判断是否是Ping消息
		if (frame instanceof PingWebSocketFrame) {
			ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
			return;
		}

		// 本例程仅支持文本消息，不支持二进制消息
		if (!(frame instanceof TextWebSocketFrame)) {
			throw new UnsupportedOperationException(
					String.format("%s frame types not supported", frame.getClass().getName()));
		}

		// 返回应答消息
		String request = ((TextWebSocketFrame) frame).text();

		if (logger.isDebugEnabled()) {
			logger.debug(String.format("%s received %s", ctx.channel(), request));
		}
		ctx.channel().write(
				new TextWebSocketFrame(request + " , 欢迎使用Netty WebSocket服务，现在时刻：" + new java.util.Date().toString()));

	}

	public static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {

		// 返回应答给客户端
		boolean isKeepAlive = HttpHeaders.isKeepAlive(req);

		if (res.getStatus().code() != 200) {

			ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
			res.content().writeBytes(buf);
			buf.release();
			// HttpUtil.setContentLength(res, res.content().readableBytes());
			if (isKeepAlive) {// 长链接

				res.headers().set(HttpHeaders.Names.CONTENT_LENGTH, res.content().readableBytes());
				res.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);

			}

			// 跨域访问许可
			res.headers().set(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
			res.headers().set(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_METHODS, "POST");
			res.headers().set(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_HEADERS, "x-requested-with,content-type");

		}

		// 如果是非Keep-Alive，关闭连接
		ChannelFuture f = ctx.channel().writeAndFlush(res);
		if (!isKeepAlive || res.getStatus().code() != 200) {
			f.addListener(ChannelFutureListener.CLOSE);
		}

	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		ctx.close();
	}

}